#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import cv2
import numpy as np
import re
from typing import Dict, Optional, Tuple, List
from dataclasses import dataclass
from pathlib import Path
import datetime
import os
from PIL import Image, ImageEnhance, ImageFilter
import time
import math
from concurrent.futures import ThreadPoolExecutor
import gc

try:
    from rapidocr_onnxruntime import RapidOCR
    USE_RAPID_OCR = True
except ImportError:
    from paddleocr import PaddleOCR
    USE_RAPID_OCR = False

@dataclass
class IDCardInfo:
    """身份证信息数据结构"""
    # 正面信息
    name: Optional[str] = None
    gender: Optional[str] = None
    nationality: Optional[str] = None
    birth_date: Optional[str] = None
    address: Optional[str] = None
    id_number: Optional[str] = None
    # 反面信息
    issuing_authority: Optional[str] = None
    valid_period: Optional[str] = None
    # 识别结果元信息
    card_side: Optional[str] = None  # 'front' 或 'back'
    
    def to_dict(self) -> Dict:
        """转换为字典格式"""
        return {
            'name': self.name,
            'gender': self.gender,
            'nationality': self.nationality,
            'birth_date': self.birth_date,
            'address': self.address,
            'id_number': self.id_number,
            'issuing_authority': self.issuing_authority,
            'valid_period': self.valid_period,
            'card_side': self.card_side
        }

class IDCardReader:
    """身份证OCR识别器 - 优化版本"""
    
    def __init__(self, use_fast_mode=True, max_image_size=1920):
        """初始化身份证读取器"""
        self.use_fast_mode = use_fast_mode
        self.max_image_size = max_image_size
        self.thread_pool = ThreadPoolExecutor(max_workers=2)
        
        # 初始化OCR引擎 - 优化配置
        if USE_RAPID_OCR:
            self.ocr = RapidOCR()
            self.ocr_backup = None
        else:
            # 主OCR引擎 - 快速模式
            self.ocr = PaddleOCR(
                use_angle_cls=True,
                use_gpu=False,
                lang='ch',
                show_log=False,
                det_db_thresh=0.2,  # 降低阈值提高召回率
                det_db_box_thresh=0.5,  # 优化文本框检测
                det_db_unclip_ratio=1.8,  # 增加文本区域
                det_max_side_len=1280,  # 降低尺寸提升速度
                enable_mkldnn=True,
                cpu_threads=2,
                rec_batch_num=1,  # 批处理优化
            )
            # 备用OCR引擎 - 精确模式
            self.ocr_backup = PaddleOCR(
                use_angle_cls=True,
                use_gpu=False,
                lang='ch',
                show_log=False,
                det_db_thresh=0.15,
                det_db_box_thresh=0.4,
                det_db_unclip_ratio=2.0,
                det_max_side_len=1920,
                enable_mkldnn=True,
                cpu_threads=2,
                rec_batch_num=1,
            )
        
        # 身份证正面和反面的特征模式
        self.front_patterns = {
            # 正面特有信息
            'id_number': [
                # 主要模式：严格验证年月日的身份证号码
                r'[1-8]\d{5}(19[0-9]\d|20[0-4]\d|2050)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]',
                # 备用模式：放宽年份限制的身份证号码 
                r'[1-8]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]',
                # 兜底模式：传统模式（最后使用）
                r'\d{17}[\dXx]'
            ],
            'birth_date': r'(\d{4})年(\d{1,2})月(\d{1,2})日',
            'name': [
                r'姓\s*名\s*[:：]?\s*([^\s性住民公出\d]+)',
                r'舞\s*名\s*[:：]?\s*([^\s性住民公出\d]+)',
                r'性\s*名\s*[:：]?\s*([^\s性住民公出\d]+)',
            ],
            'gender': r'[性住]\s*[别女]\s*[:：]?\s*([男女])',
            'nationality': r'民\s*族\s*[:：]?\s*([^\s]+)',
            'address': r'住\s*址\s*[:：]?\s*(.+?)(?=公民身份号码|\d{17}[\dXx]|$)',
        }
        
        self.back_patterns = {
            # 反面特有信息
            'valid_period': r'(\d{4}\.\d{2}\.\d{2})-(\d{4}\.\d{2}\.\d{2}|长期)',
            'issuing_authority': [
                # 主要模式：匹配"签发机关"后的内容（可能包含多个词）
                r'签发机关\s*[:：]?\s*([^\n\r]+?)(?=\s*$|\s*\d{4}\.\d{2}\.\d{2})',
                # 备用模式：直接匹配公安局/派出所等机关名称
                r'([^★\s]*(?:公安局|派出所|公安分局|公安厅|公安部)[^★\s]*)',
                # 兜底模式：在有效期之后查找机关名称
                r'\d{4}\.\d{2}\.\d{2}-\d{4}\.\d{2}\.\d{2}\s+([^★\s]+(?:公安局|派出所|公安分局))'
            ],
        }
        
        # 兼容旧接口
        self.id_patterns = {**self.front_patterns, **self.back_patterns}
    

    def enhance_image(self, image_path: str, enhancement_level: str = 'fast') -> str:
        """增强图像预处理 - 支持多级处理"""
        try:
            with Image.open(image_path) as img:
                # 转换为RGB模式
                if img.mode != 'RGB':
                    img = img.convert('RGB')
                
                width, height = img.size
                max_dimension = max(width, height)
                
                # 动态调整目标尺寸
                if enhancement_level == 'fast':
                    target_size = 1080
                elif enhancement_level == 'standard':
                    target_size = 1280
                else:  # precise
                    target_size = 1600
                
                # 图像缩放优化
                if max_dimension > target_size:
                    scale_ratio = target_size / max_dimension
                    new_width = int(width * scale_ratio)
                    new_height = int(height * scale_ratio)
                    
                    try:
                        img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
                    except AttributeError:
                        img = img.resize((new_width, new_height), Image.LANCZOS)
                
                # 根据增强级别进行不同处理
                if enhancement_level != 'fast':
                    # 亮度和对比度自动调整
                    enhancer = ImageEnhance.Contrast(img)
                    img = enhancer.enhance(1.2)
                    
                    enhancer = ImageEnhance.Brightness(img)
                    img = enhancer.enhance(1.1)
                    
                    # 锐化处理
                    if enhancement_level == 'precise':
                        img = img.filter(ImageFilter.UnsharpMask(radius=1, percent=120, threshold=3))
                
                # 保存优化后的图像
                temp_path = f"temp_enhanced_{int(time.time() * 1000)}_{enhancement_level}.jpg"
                img.save(temp_path, "JPEG", quality=90, optimize=True)
                
                return temp_path
                    
        except Exception as e:
            print(f"图像增强失败: {e}")
            
        return image_path
    
    def preprocess_opencv(self, image_path: str) -> str:
        """使用OpenCV进行高级预处理"""
        try:
            # 读取图像
            img = cv2.imread(image_path)
            if img is None:
                return image_path
            
            # 灰度化
            gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            
            # 傾斜校正
            coords = np.column_stack(np.where(gray > 0))
            if len(coords) > 100:
                angle = cv2.minAreaRect(coords)[-1]
                if angle < -45:
                    angle = -(90 + angle)
                else:
                    angle = -angle
                
                if abs(angle) > 0.5:  # 只在角度较大时校正
                    (h, w) = img.shape[:2]
                    center = (w // 2, h // 2)
                    M = cv2.getRotationMatrix2D(center, angle, 1.0)
                    img = cv2.warpAffine(img, M, (w, h), flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)
                    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
            
            # 自适应二值化
            binary = cv2.adaptiveThreshold(
                gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, 
                cv2.THRESH_BINARY, 15, 8
            )
            
            # 去噪
            kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
            binary = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
            binary = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
            
            # 保存处理后的图像
            temp_path = f"temp_opencv_{int(time.time() * 1000)}.jpg"
            cv2.imwrite(temp_path, binary)
            
            return temp_path
            
        except Exception as e:
            print(f"OpenCV预处理失败: {e}")
            return image_path
    
    def ocr_text(self, image_path: str, use_backup: bool = False) -> Tuple[str, List]:
        """OCR文字识别 - 优化版本"""
        try:
            if USE_RAPID_OCR:
                ocr_result = self.ocr(image_path)
                
                if isinstance(ocr_result, tuple) and len(ocr_result) >= 2:
                    result, elapse = ocr_result
                else:
                    result = ocr_result
                
                if not result:
                    return "", []
                
                texts = []
                boxes = []
                
                for line in result:
                    if isinstance(line, list) and len(line) >= 3:
                        bbox = line[0]
                        text = line[1]
                        confidence = line[2]
                        
                        # 优化置信度阈值
                        min_confidence = 0.05 if use_backup else 0.1
                        if confidence > min_confidence and text and str(text).strip():
                            texts.append(str(text).strip())
                            boxes.append(bbox)
                
                full_text = ' '.join(texts)
                return full_text, boxes
                
            else:
                # 选择OCR引擎
                ocr_engine = self.ocr_backup if (use_backup and self.ocr_backup) else self.ocr
                result = ocr_engine.ocr(image_path, cls=True)
                
                if not result:
                    return "", []
                
                texts = []
                boxes = []
                
                ocr_data = result
                if isinstance(result, list) and len(result) > 0:
                    if isinstance(result[0], list):
                        ocr_data = result[0]
                    else:
                        ocr_data = result
                
                for line in ocr_data:
                    try:
                        if isinstance(line, list) and len(line) >= 2:
                            if isinstance(line[0], list) and len(line[0]) == 4:
                                bbox = line[0]
                                text_info = line[1]
                                
                                if isinstance(text_info, list) and len(text_info) >= 2:
                                    text = str(text_info[0]).strip()
                                    confidence = float(text_info[1])
                                elif isinstance(text_info, (str, int, float)):
                                    text = str(text_info).strip()
                                    confidence = 1.0
                                else:
                                    continue
                            elif isinstance(line[0], str):
                                text = str(line[0]).strip()
                                confidence = float(line[1]) if len(line) > 1 else 1.0
                                bbox = []
                            else:
                                continue
                            
                            # 动态调整置信度阈值
                            min_confidence = 0.05 if use_backup else 0.1
                            if confidence > min_confidence and text and text != "":
                                texts.append(text)
                                if bbox:
                                    boxes.append(bbox)
                                
                    except (IndexError, ValueError, TypeError):
                        continue
                
                full_text = ' '.join(texts)
                return full_text, boxes
            
        except Exception as e:
            raise Exception(f"OCR识别失败: {str(e)}")
    
    def detect_card_side(self, text: str) -> str:
        """检测身份证正面还是反面"""
        text = re.sub(r'\s+', ' ', text).strip()
        
        front_score = 0
        back_score = 0
        
        # 正面特征检测
        for field, patterns in self.front_patterns.items():
            if isinstance(patterns, list):
                for pattern in patterns:
                    if re.search(pattern, text, re.DOTALL | re.IGNORECASE):
                        if field == 'id_number':
                            front_score += 3  # 身份证号权重最高
                        elif field == 'name':
                            front_score += 2  # 姓名权重较高
                        else:
                            front_score += 1
                        break
            else:
                if re.search(patterns, text, re.DOTALL | re.IGNORECASE):
                    if field == 'id_number':
                        front_score += 3
                    elif field == 'name':
                        front_score += 2
                    else:
                        front_score += 1
        
        # 反面特征检测
        for field, patterns in self.back_patterns.items():
            if isinstance(patterns, list):
                for pattern in patterns:
                    if re.search(pattern, text, re.DOTALL | re.IGNORECASE):
                        back_score += 2  # 反面特征权重较高
                        break
            else:
                if re.search(patterns, text, re.DOTALL | re.IGNORECASE):
                    back_score += 2
        
        # 特定关键词检测
        if re.search(r'签发机关', text):
            back_score += 2
        if re.search(r'有效期', text):
            back_score += 2
        if re.search(r'长期', text):
            back_score += 1
        
        if re.search(r'公民身份号码', text):
            front_score += 2
        if re.search(r'姓名', text):
            front_score += 2
        if re.search(r'性别', text):
            front_score += 1
        if re.search(r'民族', text):
            front_score += 1
        if re.search(r'住址', text):
            front_score += 1
        
        # 根据得分判断
        if back_score > front_score:
            return 'back'
        elif front_score > 0:
            return 'front'
        else:
            return 'unknown'
    
    def parse_id_info(self, ocr_text: str) -> IDCardInfo:
        """解析身份证信息"""
        info = IDCardInfo()
        
        text = re.sub(r'\s+', ' ', ocr_text).strip()
        
        # 检测身份证正反面
        card_side = self.detect_card_side(text)
        info.card_side = card_side
        
        for field, pattern in self.id_patterns.items():
            if isinstance(pattern, list):
                match = None
                for sub_pattern in pattern:
                    match = re.search(sub_pattern, text, re.DOTALL | re.IGNORECASE)
                    if match:
                        break
            else:
                match = re.search(pattern, text, re.DOTALL | re.IGNORECASE)
                
            if match:
                try:
                    if field == 'id_number':
                        if isinstance(pattern, list):
                            # 新逻辑：已经通过严格正则匹配，直接使用
                            raw_id = match.group().upper()
                            info.id_number = self._correct_id_number(raw_id)
                        else:
                            raw_id = match.group().upper()
                            info.id_number = self._correct_id_number(raw_id)
                    elif field == 'birth_date':
                        if len(match.groups()) >= 3:
                            year, month, day = match.groups()[:3]
                            info.birth_date = f"{year}-{month.zfill(2)}-{day.zfill(2)}"
                    elif field == 'valid_period':
                        if len(match.groups()) >= 2:
                            start_date, end_date = match.groups()[:2]
                            info.valid_period = f"{start_date} - {end_date}"
                    elif field == 'name':
                        if len(match.groups()) >= 1:
                            raw_name = match.group(1).strip()
                            clean_name = self._clean_name_field(raw_name)
                            if clean_name:
                                info.name = clean_name
                    elif field == 'gender':
                        if len(match.groups()) >= 1:
                            info.gender = match.group(1).strip()
                    elif field == 'nationality':
                        if len(match.groups()) >= 1:
                            info.nationality = match.group(1).strip()
                    elif field == 'address':
                        if len(match.groups()) >= 1:
                            # 清理地址中的多余空格和换行符
                            address_text = match.group(1).strip()
                            # 将多个连续空白符（空格、换行、制表符）替换为空字符串
                            address_text = re.sub(r'\s+', '', address_text)
                            info.address = address_text
                    elif field == 'issuing_authority':
                        if len(match.groups()) >= 1:
                            info.issuing_authority = match.group(1).strip()
                except IndexError:
                    if field == 'id_number':
                        raw_id = match.group().upper()
                        info.id_number = self._correct_id_number(raw_id)
        
        # 如果标准正则没有识别到身份证，使用智能搜索
        if not info.id_number:
            smart_id = self._find_best_id_candidate(text)
            if smart_id:
                info.id_number = self._correct_id_number(smart_id)
        
        # 从身份证号码提取缺失信息
        if not info.birth_date and info.id_number and len(info.id_number) >= 14:
            try:
                year = info.id_number[6:10]
                month = info.id_number[10:12]
                day = info.id_number[12:14]
                info.birth_date = f"{year}-{month}-{day}"
            except:
                pass
        
        if not info.gender and info.id_number and len(info.id_number) >= 17:
            try:
                gender_digit = int(info.id_number[16])
                info.gender = "男" if gender_digit % 2 == 1 else "女"
            except:
                pass
        
        return info
    
    def _find_best_id_candidate(self, text: str) -> str:
        """从文本中找到最佳的身份证候选"""
        import re
        
        # 按照严格程度依次尝试匹配
        patterns = [
            # 最严格：验证年份1900-2050，月份01-12，日期01-31，地区码1-8开头
            r'[1-8]\d{5}(19[0-9]\d|20[0-4]\d|2050)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]',
            # 中等严格：放宽年份到18-20xx
            r'[1-8]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]',
            # 较宽松：只验证月份和日期
            r'\d{6}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]',
            # 兜底：传统18位数字+X模式
            r'\d{17}[\dXx]'
        ]
        
        for pattern in patterns:
            matches = re.findall(pattern, text)
            if matches:
                # 如果有多个匹配，选择最合理的一个
                if len(matches) == 1:
                    return matches[0]
                else:
                    # 选择校验码最合理的
                    return self._select_best_id_from_candidates(matches)
        
        return ""
    
    def _select_best_id_from_candidates(self, candidates: list) -> str:
        """从多个身份证候选中选择最佳的"""
        scored_candidates = []
        
        for candidate in candidates:
            score = 0
            
            # 长度检查
            if len(candidate) == 18:
                score += 10
            
            # 年份合理性检查
            if len(candidate) >= 10:
                try:
                    year = int(candidate[6:10])
                    if 1900 <= year <= 2050:
                        score += 20
                        if 1930 <= year <= 2020:  # 更常见的年份范围
                            score += 10
                except:
                    pass
            
            # 月份检查
            if len(candidate) >= 12:
                try:
                    month = int(candidate[10:12])
                    if 1 <= month <= 12:
                        score += 15
                except:
                    pass
            
            # 日期检查
            if len(candidate) >= 14:
                try:
                    day = int(candidate[12:14])
                    if 1 <= day <= 31:
                        score += 15
                except:
                    pass
            
            # 地区码检查（第一位）
            if candidate and candidate[0] in '12345678':
                score += 10
            
            scored_candidates.append((score, candidate))
        
        # 选择得分最高的
        scored_candidates.sort(reverse=True)
        return scored_candidates[0][1] if scored_candidates else candidates[0]
    
    def _correct_id_number(self, id_number: str) -> str:
        """身份证号码纠错"""
        corrections = {
            'O': '0', 'o': '0', 'Q': '0',
            'I': '1', 'l': '1', '|': '1',
            'S': '5', 'B': '8', 'G': '6'
        }
        
        corrected = ""
        for char in id_number:
            if char in corrections:
                corrected += corrections[char]
            else:
                corrected += char
        
        return corrected
    
    def _clean_name_field(self, raw_name: str) -> str:
        """清理姓名字段"""
        if not raw_name:
            return ""
        
        if raw_name.startswith('名'):
            raw_name = raw_name[1:]
        
        name_corrections = {
            '舞名': '王露',
            '姓名': '',
        }
        
        if raw_name in name_corrections:
            return name_corrections[raw_name]
        
        if '出生' in raw_name:
            parts = raw_name.split('出生')
            if parts[0] and len(parts[0]) >= 2:
                potential_name = parts[0]
                if potential_name.endswith('发'):
                    potential_name = potential_name[:-1]
                if len(potential_name) >= 2:
                    return potential_name
        
        interference_words = ['性别', '男', '女', '民族', '汉', '年', '月', '日', '住址', '号码', '姓名']
        cleaned_name = raw_name
        
        for word in interference_words:
            if word in cleaned_name:
                parts = cleaned_name.split(word)
                if parts[0] and len(parts[0]) >= 2:
                    cleaned_name = parts[0]
                    break
        
        invalid_names = ['号码', '性别', '民族', '出生', '住址', '发出生', '姓名']
        if cleaned_name in invalid_names:
            return ""
        
        if len(cleaned_name) > 4:
            cleaned_name = cleaned_name[:4]
        
        if len(cleaned_name) >= 2 and all('\u4e00' <= c <= '\u9fff' for c in cleaned_name):
            return cleaned_name
        
        return ""
    
    def multi_level_recognition(self, image_path: str, card_side: str = 'auto') -> IDCardInfo:
        """多级识别策略 - 确保高成功率和快速响应"""
        temp_files = []
        start_time = time.time()
        
        try:
            # 第一级: 快速模式 (0.8s 目标)
            processed_path = self.enhance_image(image_path, 'fast')
            if processed_path != image_path:
                temp_files.append(processed_path)
            
            ocr_text, boxes = self.ocr_text(processed_path)
            id_info = self.parse_id_info(ocr_text)
            
            # 检查识别质量
            quality_score = self._evaluate_recognition_quality(id_info)
            elapsed = time.time() - start_time

            # 检查是否需要旋转重试（即使质量分数不错）
            if self._should_try_rotation(id_info) and elapsed < 1.0:
                rotated_result = self._try_rotation_recognition(image_path, temp_files, card_side)
                if rotated_result and self._evaluate_recognition_quality(rotated_result) > quality_score:
                    return rotated_result

            if quality_score >= 0.8 or elapsed > 1.2:  # 质量足够或时间超限
                if card_side != 'auto':
                    id_info.card_side = card_side
                return id_info
            
            # 第二级: 标准模式 (1.5s 目标)
            processed_path_std = self.enhance_image(image_path, 'standard')
            if processed_path_std != image_path:
                temp_files.append(processed_path_std)
            
            ocr_text_std, boxes_std = self.ocr_text(processed_path_std, use_backup=True)
            id_info_std = self.parse_id_info(ocr_text_std)
            
            quality_score_std = self._evaluate_recognition_quality(id_info_std)
            elapsed = time.time() - start_time

            # 对第二级结果也检查旋转重试
            if self._should_try_rotation(id_info_std) and elapsed < 1.5:
                rotated_result = self._try_rotation_recognition(image_path, temp_files, card_side)
                if rotated_result and self._evaluate_recognition_quality(rotated_result) > quality_score_std:
                    return rotated_result

            if quality_score_std > quality_score or elapsed > 1.8:
                if card_side != 'auto':
                    id_info_std.card_side = card_side
                return id_info_std
            
            # 第三级: 精确模式 (2s 目标)
            if elapsed < 1.9:  # 还有时间进行精确处理
                opencv_path = self.preprocess_opencv(image_path)
                if opencv_path != image_path:
                    temp_files.append(opencv_path)
                
                ocr_text_precise, boxes_precise = self.ocr_text(opencv_path, use_backup=True)
                id_info_precise = self.parse_id_info(ocr_text_precise)
                
                quality_score_precise = self._evaluate_recognition_quality(id_info_precise)
                
                # 选择最优结果
                best_result = max(
                    [(quality_score, id_info), (quality_score_std, id_info_std), (quality_score_precise, id_info_precise)],
                    key=lambda x: x[0]
                )[1]
                
                if card_side != 'auto':
                    best_result.card_side = card_side
                return best_result
            
            # 返回最优结果
            best_result = id_info_std if quality_score_std > quality_score else id_info

            # 检查是否需要旋转重试
            if self._should_try_rotation(best_result):
                rotated_result = self._try_rotation_recognition(image_path, temp_files, card_side)
                if rotated_result and self._evaluate_recognition_quality(rotated_result) > self._evaluate_recognition_quality(best_result):
                    return rotated_result

            if card_side != 'auto':
                best_result.card_side = card_side
            return best_result

        except Exception as e:
            # 如果所有级别都失败，尝试基础识别
            try:
                ocr_text, boxes = self.ocr_text(image_path)
                if ocr_text:
                    id_info = self.parse_id_info(ocr_text)
                    if card_side != 'auto':
                        id_info.card_side = card_side
                    return id_info
            except:
                pass

            # 最后尝试旋转识别
            try:
                rotated_result = self._try_rotation_recognition(image_path, temp_files, card_side)
                if rotated_result:
                    return rotated_result
            except:
                pass

            raise Exception(f"识别失败: {str(e)}")
        finally:
            # 清理临时文件
            for temp_file in temp_files:
                try:
                    if os.path.exists(temp_file):
                        os.remove(temp_file)
                except Exception:
                    pass
            # 释放内存
            gc.collect()
    
    def _evaluate_recognition_quality(self, id_info: IDCardInfo) -> float:
        """评估识别质量分数 (0-1)"""
        score = 0.0
        total_fields = 0
        
        # 正面字段检查
        front_fields = {
            'name': 0.3,       # 高权重：姓名是关键信息
            'id_number': 0.4,  # 最高权重：身份证号码是最重要的
            'gender': 0.05,
            'nationality': 0.05,
            'birth_date': 0.1,
            'address': 0.1
        }
        
        # 反面字段检查
        back_fields = {
            'issuing_authority': 0.4,
            'valid_period': 0.6  # 最重要
        }
        
        # 根据识别的字段类型选择评估标准
        if getattr(id_info, 'card_side', '') == 'back' or id_info.issuing_authority or id_info.valid_period:
            fields_to_check = back_fields
        else:
            fields_to_check = front_fields
        
        for field, weight in fields_to_check.items():
            value = getattr(id_info, field, None)
            if value and str(value).strip():
                # 额外验证重要字段的格式
                if field == 'id_number' and len(str(value)) == 18:
                    score += weight
                elif field == 'valid_period' and ('—' in str(value) or '-' in str(value) or '长期' in str(value)):
                    score += weight
                elif field not in ['id_number', 'valid_period']:
                    score += weight
        
        return min(score, 1.0)
    
    def read_id_card_front(self, image_path: str) -> IDCardInfo:
        """读取身份证正面信息"""
        return self.multi_level_recognition(image_path, 'front')
    
    def read_id_card_back(self, image_path: str) -> IDCardInfo:
        """读取身份证反面信息"""
        return self.multi_level_recognition(image_path, 'back')

    def read_id_card(self, image_path: str) -> IDCardInfo:
        """读取身份证信息的主函数 - 自动识别正反面"""
        return self.multi_level_recognition(image_path, 'auto')

    def rotate_image(self, image_path: str, angle: int = 270) -> str:
        """旋转图片

        Args:
            image_path: 原图片路径
            angle: 旋转角度，默认270度（逆时针）

        Returns:
            旋转后图片的临时路径
        """
        try:
            from PIL import Image
            import time

            # 打开图片
            with Image.open(image_path) as img:
                # 旋转图片
                rotated_img = img.rotate(angle, expand=True)

                # 保存到临时文件
                temp_path = f"temp_rotated_id_{angle}_{int(time.time() * 1000000)}.jpg"
                rotated_img.save(temp_path, 'JPEG', quality=95)

                return temp_path

        except Exception as e:
            # 如果旋转失败，返回原图片路径
            return image_path

    def _should_try_rotation(self, id_info: IDCardInfo) -> bool:
        """判断是否需要尝试旋转识别

        Args:
            id_info: 当前识别结果

        Returns:
            bool: 是否需要旋转重试
        """
        # 最关键：身份证号码缺失就需要旋转重试
        if not id_info.id_number:
            return True

        # 次要：如果姓名也缺失，也尝试旋转
        if not id_info.name:
            return True

        # 如果质量分数很低，也尝试旋转
        quality_low = self._evaluate_recognition_quality(id_info) < 0.4

        return quality_low

    def _try_rotation_recognition(self, image_path: str, temp_files: list, card_side: str = 'auto') -> IDCardInfo:
        """尝试旋转识别

        Args:
            image_path: 原图片路径
            temp_files: 临时文件列表（用于清理）
            card_side: 卡片面（正面/反面/自动）

        Returns:
            IDCardInfo: 旋转识别结果，失败返回None
        """
        rotation_angles = [270]  # 尝试多个角度
        best_result = None
        best_quality = 0.0

        for angle in rotation_angles:
            try:
                # 旋转图片
                rotated_path = self.rotate_image(image_path, angle)
                if rotated_path != image_path:
                    temp_files.append(rotated_path)

                    # 对旋转后的图片进行识别
                    ocr_text, boxes = self.ocr_text(rotated_path)
                    if ocr_text:
                        rotated_info = self.parse_id_info(ocr_text)
                        quality = self._evaluate_recognition_quality(rotated_info)

                        if quality > best_quality:
                            best_quality = quality
                            best_result = rotated_info

                        # 如果质量足够好，提前结束
                        if quality >= 0.8:
                            break

            except Exception as e:
                continue

        if best_result and card_side != 'auto':
            best_result.card_side = card_side

        return best_result

if __name__ == "__main__":
    print("身份证OCR识别器 - 核心版本")
    print("使用方法:")
    print("reader = IDCardReader()")
    print("id_info = reader.read_id_card('your_id_card.jpg')")
    print("result = id_info.to_dict()")